﻿using System;

namespace POCOFirst
{
	public interface IDecorator
	{
		void InitDecorator(ICalculator decoratee);
		ICalculator Decoratee { get; }
	}

	public abstract class Decorator : IDecorator
	{
		private ICalculator _decoratee;

		public void InitDecorator(ICalculator decoratee)
		{
			this._decoratee = decoratee;
		}

		public ICalculator Decoratee
		{
			get { return this._decoratee; }
		}
	}

	public class CalculatorWithLogger : Decorator, ICalculator
	{
		public int Add(int x, int y)
		{
			Console.Write("{0} + {1}", x, y);
			var result = Decoratee.Add(x, y);
			Console.WriteLine(" = {0}", result);
			return result;
		}

		public int Subtract(int x, int y)
		{
			Console.Write("{0} - {1}", x, y);
			var result = Decoratee.Subtract(x, y);
			Console.WriteLine(" = {0}", result);
			return result;
		}
	}

	public class CalculatorWithTimer : Decorator, ICalculator
	{
		public int Add(int x, int y)
		{
			var beginTime = DateTime.Now;
			var result = Decoratee.Add(x, y);
			var elapsedTime = DateTime.Now.Subtract(beginTime);
			Console.WriteLine(elapsedTime);
			return result;
		}

		public int Subtract(int x, int y)
		{
			var beginTime = DateTime.Now;
			var result = Decoratee.Subtract(x, y);
			var elapsedTime = DateTime.Now.Subtract(beginTime);
			Console.WriteLine(elapsedTime);
			return result;
		}
	}

	public class DecoratorFactory
	{
		private DecoratorFactory()
		{ }

		private static Type[] GetObjectGraph()
		{
			// 从里向外，添加装饰
			return new Type[]
			       	{
			       		typeof (Calculator),
			       		typeof (CalculatorWithLogger),
			       		typeof (CalculatorWithTimer)
			       	};
		}

		public static ICalculator CreateInstance()
		{
			var objectGraphTypes = GetObjectGraph();
			ICalculator result = null;

			// 所有的装饰被从里到外地挨个取出被装配
			foreach (var calcType in objectGraphTypes)
			{
				var calcImpl = Activator.CreateInstance(calcType) as ICalculator;

				if (calcImpl is IDecorator)
					(calcImpl as IDecorator).InitDecorator(result);

				result = calcImpl;
			}

			return result;
		}
	}

}